#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QDebug>
#include <QContextMenuEvent>
#include <QTextCharFormat>
#include <QFileDialog>
#include <QTimer>
#include <QMimeData>

#include "ctrlargsdialog.h"

const int defaultReadSize = 0x10;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    setWindowTitle(tr("HexMark"));
    ui->textEdit->setAcceptDrops(false);
    setAcceptDrops(true);
    resize(1100, this->height());

    m_lineSize = defaultReadSize;
    m_groupSize = 4;

    setUpColorComBox();
    setTextEdit();
    setLableColor(ui->comboBoxColor->currentText());
    setUpActions();
    setupSpinBox();

    connect(ui->pushButtonMark,&QPushButton::clicked, this, &MainWindow::markSlots);
    connect(ui->pushButtonContinueRead, &QPushButton::clicked, this, &MainWindow::continueReadSlots);
    ui->pushButtonContinueRead->hide();
    ui->spinBox->hide();


#ifdef DEBUG
    openFile(":/example-rainbow.gif");
    readFile();
#endif
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
    event->acceptProposedAction();
}
void MainWindow::dropEvent(QDropEvent *event)
{
    QList<QUrl> urls = event->mimeData()->urls();
    if (urls.isEmpty()) {
        return;
    }

    QString fileName = urls.first().toLocalFile();
    if (fileName.isEmpty()) {
        return;
    }

    if(openFile(fileName)) {
        readFile();
        setWindowTitle(tr("%1 - %2").arg(fileName, tr("Drag File")));
    }
}

void MainWindow::markSlots()
{
    if(ui->textEdit->textCursor().selectedText().length() == 0) {
        return;
    }

    QTextCharFormat fmt;
    QColor color(ui->comboBoxColor->currentText());
    fmt.setBackground(color);
    QTextCursor cursor = ui->textEdit->textCursor();
    cursor.select(QTextCursor::WordUnderCursor);
    cursor.mergeBlockCharFormat(fmt);
    ui->textEdit->mergeCurrentCharFormat(fmt);

    QTimer::singleShot(0,[this](){
        ui->textEdit->setFocus();
        ui->textEdit->textCursor().clearSelection();
    });
}


void MainWindow::setUpColorComBox()
{
    ui->comboBoxColor->addItems(QColor::colorNames());

    QObject::connect(ui->comboBoxColor, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),[this](int  ){
        QColor color(ui->comboBoxColor->currentText());
        ui->labelcolor->setAutoFillBackground(true);
        ui->labelcolor->setPalette(color);
    });
}


void MainWindow::setTextEdit()
{
    QFont font = QFont("Consolas",20,2);
    ui->textEdit->setFont(font);
    ui->textEdit->setReadOnly(true);
}

void MainWindow::setLableColor(QString colorName)
{
    QColor color(colorName);
    ui->labelcolor->setAutoFillBackground(true);
    ui->labelcolor->setPalette(color);
}

void MainWindow::setUpActions()
{
    setupFileActions();
    setupEditActions();
//    setCtrlActions();
}

void MainWindow::setupFileActions()
{
    QMenu * fileMenu = menuBar()->addMenu(tr("&File"));
    QToolBar * fileToolBar = addToolBar(tr("File"));

    const QIcon newIcon = QIcon::fromTheme("document-new", QIcon(":/images/new.png"));
    QAction * newAct = new QAction(newIcon, tr("&New"), this);
    newAct->setShortcut(QKeySequence::New);
    newAct->setStatusTip(tr("Create a new file"));
    fileMenu->addAction(newAct);
    fileToolBar->addAction(newAct);

    const QIcon openIcon = QIcon::fromTheme("document-Open", QIcon(":/images/open.png"));
    QAction * openAct = new QAction(openIcon, tr("&Open"), this);
    openAct->setShortcut(QKeySequence::Open);
    openAct->setStatusTip(tr("Open a new file"));
    connect(openAct, &QAction::triggered, [this](){
        QString fileName =  QFileDialog::getOpenFileName();
        if(! fileName.isEmpty()) {
            openFile(fileName);
            readFile();
        }
    });
    fileMenu->addAction(openAct);
    fileToolBar->addAction(openAct);

}

void MainWindow::setupEditActions()
{
    QMenu * fileMenu = menuBar()->addMenu(tr("&Edit"));
    QToolBar * fileToolBar = addToolBar(tr("Edit"));

    const QIcon newIcon = QIcon::fromTheme("document-undo", QIcon(":/images/undo.png"));
    QAction * undoAct = new QAction(newIcon, tr("&Undo"), this);
    undoAct->setShortcut(QKeySequence::Undo);
    undoAct->setStatusTip(tr("Undo a Action"));
    connect(undoAct, &QAction::triggered, [this](){
        ui->textEdit->undo();
    });
    fileMenu->addAction(undoAct);
    fileToolBar->addAction(undoAct);

    const QIcon RedoIcon = QIcon::fromTheme("document-Redo", QIcon(":/images/redo.png"));
    QAction * redoAct = new QAction(RedoIcon, tr("&Redo"), this);
    redoAct->setShortcut(QKeySequence::Redo);
    redoAct->setStatusTip(tr("Redo a Action"));
    connect(redoAct, &QAction::triggered, [this](){
        ui->textEdit->redo();
    });
    fileMenu->addAction(redoAct);
    fileToolBar->addAction(redoAct);
}


void MainWindow::setCtrlActions()
{
    QMenu * ctrlMenu = menuBar()->addMenu(tr("Ctrl"));
    QAction * ctrlAction = new QAction(tr("&Ctrl"),this);
    ctrlMenu->setStatusTip(tr("Setup Args"));

    connect(ctrlAction, &QAction::triggered, [this](){
        CtrlArgsDialog dialog;
        connect(&dialog, &CtrlArgsDialog::sendArgs, this, &MainWindow::recvArgs);
        dialog.exec();
    });

    ctrlMenu->addAction(ctrlAction);
}
void MainWindow::setupSpinBox()
{
    ui->spinBox->setMinimum(1);
    ui->spinBox->setValue(64);
    ui->spinBox->setMaximum(200);

}


///暂时不用
void MainWindow::continueReadSlots()
{
    if(m_file.isOpen()) {
        qDebug() << m_file.pos() << m_file.size();//是否需要将这些放到 statusBar 上面？？
        int remainLen = m_file.size() - m_file.pos();
        int readLen = ui->spinBox->value();
        int realLen = remainLen > readLen ? readLen : remainLen;

        QByteArray array = m_file.read(realLen);
        if(array.isEmpty()) { //可能发生错误
            if(m_file.error() == QFileDevice::NoError) {
                if(m_file.atEnd()) {
                    ui->statusBar->showMessage(tr("Read the document to the end"));
                }
            }else {
                ui->statusBar->showMessage(m_file.errorString());
            }
        }else {
            ui->textEdit->moveCursor(QTextCursor::End);
            ui->textEdit->insertPlainText(array.toHex());
        }
    }
}

QString MainWindow::groupingString(const char * srcStr, int len, int step)
{
    QString groupString;
    for (int i = 0; i < len; i+=step) {

        for (int j = 0; j < step; ++j) {
            groupString.append(static_cast<char>(srcStr[i+j]));
        }
        groupString.append(" ");
    }

    return groupString;
}

void MainWindow::readFile()
{
    ui->textEdit->clear();

    quint64 pos = 0;
    QString formatString;
    while(1){
        pos += m_lineSize;
        formatString.sprintf("%08llx: ", pos);//拿到索引

        QByteArray array = m_file.read(m_lineSize);
        if(array.isEmpty()) {
            if(m_file.error() == QFileDevice::NoError) {
                if(m_file.atEnd()) {
                    ui->statusBar->showMessage(tr("File read"));
                }
            }else {
                ui->statusBar->showMessage(m_file.errorString());
            }
            goto readFile_end;
        }
        const char * hexData = array.toHex().data();
        QString groupString ;
        if(m_groupSize) {
            groupString = groupingString(array.toHex().data(), strlen(hexData), m_groupSize);
        }else {
            groupString = array.toHex();
            groupString.append(" ");
        }
        formatString.append(groupString);//拿到转换后的 hex 字符串
        QString asciiString = asciiFormatString(array);
        formatString.append(asciiString);//拿到转换后的ascii 字符串
        ui->textEdit->append(formatString);
    }
readFile_end:
    m_file.close();//一次性读完，读完就关闭

    ui->textEdit->moveCursor(QTextCursor::Start);

}

QString MainWindow::asciiFormatString(QByteArray &array)
{
    QString asciiString;
    for (int j = 0; j < array.length(); ++j) {

        if(array.at(j) <= 0x20) {// 040   32    20    SPACE
            asciiString.append(".");
        }else {
            asciiString.append(static_cast<char>(array.at(j)));
        }
    }

    return asciiString;
}


bool MainWindow::openFile(QString fileName)
{
    m_file.setFileName(fileName);

    bool bFlag = m_file.open(QIODevice::ReadOnly);
    if(! bFlag) {
        qDebug() << m_file.errorString();
        return false;
    }
    return true;
}


void MainWindow::recvArgs(int lineSize, int groupSize)
{
    m_lineSize = lineSize;
    m_groupSize = groupSize;

    openFile(m_file.fileName());
    readFile();
}
